﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Frogs.Stability.CircuitBreaker.Behaviors.Actions;
using Frogs.Stability.CircuitBreaker.Exceptions;
using Frogs.Stability.LeakyBucket;

namespace Frogs.Stability.CircuitBreaker
{
	/// <summary>
	/// Default implementation of Frogs.Stability.CircuitBreakerIActionCircuitBreaker
	/// </summary>
	public class DefaultActionCircuitBreaker : IActionCircuitBreaker
	{
		object _mutex = new object();

		ILeakyBucket LeakyBucket { get; set; }

		IActionCircuitBehavior _circuitBehavior = new ClosedBehavior();
		IActionCircuitBehavior CircuitBehavior
		{
			get
			{
				lock (this._mutex)
				{
					var value = this._circuitBehavior;
					return value;
				}
			}
			set
			{
				lock (this._mutex)
				{
					this._circuitBehavior = value;
				}
			}
		}

		/// <summary>
		/// Initializes a new instance of Frogs.Stability.CircuitBreaker.DefaultActionCircuitBreaker
		/// </summary>
		public DefaultActionCircuitBreaker(ILeakyBucket leakyBucket)
		{
			this.LeakyBucket = leakyBucket;
			this.LeakyBucket.WaterLevel.Subscribe(state =>
				{
					switch (state)
					{
						case BucketState.Full:
							this.SetOpenState();
							break;
						case BucketState.HalfDrained:
							this.SetHalfOpenState();
							break;
						case BucketState.Empty:
							SetClosedState();
							break;
						default:
							break;
					}
				});
		}

		void SetOpenState()
		{
			var openBehavior = new OpenBehavior();
			this.CircuitBehavior = openBehavior;
		}

		void SetHalfOpenState()
		{
			var halfOpenBehavior = new HalfOpenBehavior();
			halfOpenBehavior.ExecutionResult.Subscribe(result =>
			{
				switch (result)
				{
					case Behaviors.ExecutionResult.Success:
						this.ResetCircuit();
						break;
					case Behaviors.ExecutionResult.Fail:
						this.TripCircuit();
						break;
					default:
						break;
				}
			});
			this.CircuitBehavior = halfOpenBehavior;
		}

		void SetClosedState()
		{
			var closedBehavior = new ClosedBehavior();
			closedBehavior.ExecutionResult.Subscribe(result =>
			{
				if (result == Behaviors.ExecutionResult.Fail)
				{
					LeakyBucket.AddSingleDrop();
				}
			});
			this.CircuitBehavior = closedBehavior;
		}

		/// <summary>
		/// Attempt to perform the supplied action within the constraints of a circuit breaker.
		/// </summary>
		/// <param name="action"></param>
		public void Do(Action action)
		{
			this.CircuitBehavior.DoAction(action);
		}

		/// <summary>
		/// Reset the state of the circuit breaker to closed
		/// </summary>
		public void ResetCircuit()
		{
			this.LeakyBucket.Empty();
		}

		/// <summary>
		/// Trips the circuit breaker, setting the status to open
		/// </summary>
		public void TripCircuit()
		{
			this.LeakyBucket.FillUp();
		}

		/// <summary>
		/// Gets the state of the circuit breaker
		/// </summary>
		/// <returns></returns>
		public CircuitState QueryState()
		{
			var tmp = this.CircuitBehavior;
			return tmp.GetStatus();
		}
	}
}
